﻿using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;

using SmlEngine.Collections;

namespace SmlEngine.Timers
{
    /// <summary>
    /// Unfinished
    /// </summary>
    public sealed class TimerManager : GameComponent
    {
        #region Fields

        private readonly Pool<FrameTimer> frameTimers;
        private readonly Pool<GameTimer> gameTimers;
        private readonly Pool<Interpolator> interpolators;

        #endregion

        #region Properties

        /// <summary>
        /// The number of active frame timers.
        /// </summary>
        public int ActiveFrameTimerCount
        {
            get { return frameTimers.ValidCount; }
        }

        /// <summary>
        /// The number of active game timers.
        /// </summary>
        public int ActiveGameTimerCount
        {
            get { return gameTimers.ValidCount; }
        }

        /// <summary>
        /// The number of active interpolators.
        /// </summary>
        public int ActiveInterpolatorCount
        {
            get { return interpolators.ValidCount; }
        }

        #endregion

        /// <summary>
        /// Constructs a new TimerManager instance.
        /// </summary>
        /// <param name="game">The game that owns this TimerManager.</param>
        public TimerManager(Game game)
            : base(game)
        {
            frameTimers = new Pool<FrameTimer>(10, true, timer => timer.Enabled, () => new FrameTimer());
            gameTimers = new Pool<GameTimer>(10, true, timer => timer.Enabled, () => new GameTimer());
            interpolators = new Pool<Interpolator>(10, true, interpolator => interpolator.Enabled, () => new Interpolator());
        }

        /// <summary>
        /// Creates a FrameTimer.
        /// </summary>
        /// <param name="interval">The interval of the timer, measured in frames.</param>
        /// <param name="tick">The action to perform on each tick.</param>
        /// <returns>A new FrameTimer.</returns>
        public FrameTimer CreateFrameTimer(int interval, Action<FrameTimer> tick)
        {
            return CreateFrameTimer(interval, tick, false);
        }

        /// <summary>
        /// Creates a FrameTimer.
        /// </summary>
        /// <param name="interval">The interval of the timer, measured in frames.</param>
        /// <param name="tick">The action to perform on each tick.</param>
        /// <param name="repeats">Does this timer repeat?</param>
        /// <returns>A new FrameTimer.</returns>
        public FrameTimer CreateFrameTimer(int interval, Action<FrameTimer> tick, bool repeats)
        {
            return CreateFrameTimer(interval, tick, repeats, -1);
        }

        /// <summary>
        /// Creates a new FrameTimer.
        /// </summary>
        /// <param name="interval">The interval of the timer, measured in frames.</param>
        /// <param name="tick">The action to perform on each tick.</param>
        /// <param name="repeats">Does this timer repeat?</param>
        /// <param name="repeatsRemaining">Sets how many repeats the timer will use.</param>
        /// <returns>A new FrameTimer.</returns>
        public FrameTimer CreateFrameTimer(int interval, Action<FrameTimer> tick, bool repeats, int repeatsRemaining)
        {
            FrameTimer t = frameTimers.New();
            t.Enabled = true;
            t.Interval = interval;
            t.Tick = tick;
            t.Repeats = repeats;
            t.RepeatsRemaining = repeatsRemaining;
            return t;
        }

        /// <summary>
        /// Creates a new GameTimer.
        /// </summary>
        /// <param name="interval">The interval of the timer, measured in frames.</param>
        /// <param name="tick">The action to perform on each tick.</param>
        /// <returns>A new GameTimer.</returns>
        public GameTimer CreateGameTimer(float interval, Action<GameTimer> tick)
        {
            return CreateGameTimer(interval, tick, false);
        }

        /// <summary>
        /// Creates a new GameTimer.
        /// </summary>
        /// <param name="interval">The interval of the timer, measured in frames.</param>
        /// <param name="tick">The action to perform on each tick.</param>
        /// <param name="repeats">Does this timer repeat?</param>
        /// <returns>A new GameTimer.</returns>
        public GameTimer CreateGameTimer(float interval, Action<GameTimer> tick, bool repeats)
        {
            return CreateGameTimer(interval, tick, repeats, -1);
        }

        /// <summary>
        /// Creates a new GameTimer
        /// </summary>
        /// <param name="interval">The interval of the timer, measured in frames.</param>
        /// <param name="tick">The action to perform on each tick.</param>
        /// <param name="repeats">Does this timer repeat?</param>
        /// <param name="repeatsRemaining">How many times does this timer repeat?</param>
        /// <returns>A new GameTimer.</returns>
        public GameTimer CreateGameTimer(float interval, Action<GameTimer> tick, bool repeats, int repeatsRemaining)
        {
            GameTimer t = gameTimers.New();
            t.Enabled = true;
            t.Interval = interval;
            t.Tick = tick;
            t.Repeats = repeats;
            t.RepeatsRemaining = repeatsRemaining;
            return t;
        }

        /// <summary>
        /// Creates an Interpolator.
        /// </summary>
        /// <param name="endValue">The end state.</param>
        /// <param name="length">How long the interpolation lasts.</param>
        /// <param name="step">An action to preform on each step.</param>
        /// <returns>A new Interpolator.</returns>
        public Interpolator CreateInterpolator(float endValue, float length, Action<Interpolator> step)
        {
            return CreateInterpolator(0, endValue, length, step);
        }

        /// <summary>
        /// Creates an Interpolator.
        /// </summary>
        /// <param name="startValue">The beginning state.</param>
        /// <param name="endValue">The end state.</param>
        /// <param name="length">How long the interpolation lasts.</param>
        /// <param name="step">An action to perform on each step.</param>
        /// <returns>A new Interpolator.</returns>
        public Interpolator CreateInterpolator(float startValue, float endValue, float length, Action<Interpolator> step)
        {
            return CreateInterpolator(startValue, endValue, length, step, InterpolatorScales.Linear);
        }

        /// <summary>
        /// Creates an Interpolator.
        /// </summary>
        /// <param name="startValue">The beginning state.</param>
        /// <param name="endValue">The end state.</param>
        /// <param name="length">How long the interpolation lasts.</param>
        /// <param name="step">An action to perform on each step.</param>
        /// <param name="scale">The scale of this Interpolator.</param>
        /// <returns>A new Interpolator.</returns>
        public Interpolator CreateInterpolator(float startValue, float endValue, float length,
            Action<Interpolator> step, InterpolatorScaleDelegate scale)
        {
            return CreateInterpolator(startValue, endValue, length, step, null, scale);
        }

        /// <summary>
        /// Creates an Interpolator.
        /// </summary>
        /// <param name="startValue">The beginning state.</param>
        /// <param name="endValue">The end state.</param>
        /// <param name="length">How long the interpolation lasts.</param>
        /// <param name="step">An action to perform on each step.</param>
        /// <param name="completed">An action to perform once the Interpolator finishes.</param>
        /// <returns>A new Interpolator.</returns>
        public Interpolator CreateInterpolator(float startValue, float endValue, float length, 
            Action<Interpolator> step, Action<Interpolator> completed)
        {
            return CreateInterpolator(startValue, endValue, length, step, completed, InterpolatorScales.Linear);
        }

        /// <summary>
        /// Creates an Interpolator.
        /// </summary>
        /// <param name="startValue">The beginning state.</param>
        /// <param name="endValue">The end state.</param>
        /// <param name="length">How long the interpolation lasts.</param>
        /// <param name="step">An action to perform on each step.</param>
        /// <param name="completed">An action to perform once the Interpolator finishes.</param>
        /// <param name="scale">The scale of this Interpolator.</param>
        /// <returns>A new Interpolator.</returns>
        public Interpolator CreateInterpolator(float startValue, float endValue, float length, 
            Action<Interpolator> step, Action<Interpolator> completed, InterpolatorScaleDelegate scale)
        {
            Interpolator i = interpolators.New();
            i.Reset(startValue, endValue, length, step, completed, scale);
            return i;
        }

        /// <summary>
        /// Creates an Interpolator for colors.
        /// </summary>
        /// <param name="start">The starting color.</param>
        /// <param name="end">The ending color.</param>
        /// <param name="length">The length of interpolation.</param>
        /// <param name="setColor">The action that sets the color on the object.</param>
        /// <param name="completed">An action to perform when the Interpolator finishes.</param>
        /// <param name="scale">The scale of the Interpolator.</param>
        /// <returns>A new Interpolator.</returns>
        public Interpolator InterpolateColor(Color start, Color end, float length, 
            Action<Color> setColor, Action<Interpolator> completed, InterpolatorScaleDelegate scale)
        {
            return CreateInterpolator(0, 1, length, i => setColor(Color.Lerp(start, end, i.Value)), completed, scale);
        }

        /// <summary>
        /// Updates all timers.
        /// </summary>
        /// <param name="gameTime">Timing information for the game.</param>
        public override void Update(GameTime gameTime)
        {
            lock (frameTimers)
            {
                for (int i = 0; i < frameTimers.ValidCount; i++)
                    frameTimers[i].Update();
                frameTimers.CleanUp();
            }

            lock (gameTimers)
            {
                for (int i = 0; i < gameTimers.ValidCount; i++)
                    gameTimers[i].Update(gameTime);
                gameTimers.CleanUp();
            }

            lock (interpolators)
            {
                for (int i = 0; i < interpolators.ValidCount; i++)
                    interpolators[i].Update(gameTime);
                interpolators.CleanUp();
            }

            base.Update(gameTime);
        }

        internal void Clear()
        {
            lock (frameTimers)
            {
                for (int i = 0; i < frameTimers.ValidCount; i++)
                    frameTimers[i].Stop();
                frameTimers.CleanUp();
            }

            lock (gameTimers)
            {
                for (int i = 0; i < gameTimers.ValidCount; i++)
                    gameTimers[i].Stop();
                gameTimers.CleanUp();
            }

            lock (interpolators)
            {
                for (int i = 0; i < interpolators.ValidCount; i++)
                    interpolators[i].Stop();
                interpolators.CleanUp();
            }
        }
    }
}
